/*
 * WorkConnectApi.cpp
 *
 *  Created on: 2016-2-24
 *      Author: root
 */
#include"WorkConnectApi.h"
#if USE_PUBLIC_CONNECT
#include"PublicIPTool.h"
#include"WorkPriApi.h"
#ifndef NOUSE_PUBLIC_LOG
#include"PublicLog.h"
#endif
#include"PublicSocket.h"
#include"PublicNetHead.h"
#ifdef __MINGW32__
#else
#include<sys/resource.h>
#include<errno.h>
#include<string>
#endif
void ShowPortInfo(SETCONNECTINFO list){
    SETCONNECTINFO::iterator it2=list.begin();
    while(it2!=list.end()){
		INFO("IP:%s:%hu",it2->IP,it2->Port);
		it2++;
    }
}
void ShowMapPortINFO(MAPCONNECTINFO transmitqueue){
    MAPCONNECTINFO::iterator it=transmitqueue.begin();
    while(it!=transmitqueue.end()){
		INFOEX("源IP:%s 数量:%zd",it->first.c_str(),it->second.size());
		ShowPortInfo(it->second);
		it++;
    }
}
MAPCONNECTRESULTINFO static  _GetConnectInfoByIPList(SETCONNECTINFO IPList,size_t MaxWaitList,int MaxTimeOut,const char*SrcIP);
MAPCONNECTRESULTINFO GetConnectInfoByIPList(SETCONNECTINFO IPList,size_t MaxWaitList,int MaxTimeOut,const char*SrcIP){
	MAPCONNECTRESULTINFO result;
	int bSupportIPV6=IsSupportIPV6();
	INFO("数量:%zd 最大等待数:%zd 超时:%d s 源IP:%s IPV6支持:%d",IPList.size(),MaxWaitList,MaxTimeOut,SrcIP,bSupportIPV6);
	ReSetStartTime();
	if(bSupportIPV6==1){
		result=_GetConnectInfoByIPList(IPList,MaxWaitList,MaxTimeOut,SrcIP);
	}else{
		SETCONNECTINFO RealIPList;
		SETCONNECTINFO::iterator it=IPList.begin();
		while(it!=IPList.end()){
			if(IsIPV6(it->IP)==0){
				RealIPList.insert(*it);
			}
			it++;
		}
		result=_GetConnectInfoByIPList(RealIPList,MaxWaitList,MaxTimeOut,SrcIP);
	}
	INFO("Connect 数量:%zd 结果数量:%zd 源IP:%s 耗时:%d ms",IPList.size(),result.size(),SrcIP,GetEndTime());
	return result;
}
//最大等待队列
//最大超时单位s
MAPCONNECTRESULTINFO GetConnectInfoByIPMAPList(MAPCONNECTINFO IPMapList,size_t MaxWaitList,int MaxTimeOut){
	MAPCONNECTRESULTINFO result;
	MAPCONNECTINFO::iterator it=IPMapList.begin();
	while(it!=IPMapList.end()){
//活跃且小于10m的不再重探
		SETCONNECTINFO IPList;
		if(result.empty()){
			IPList=it->second;
		}else{
			SETCONNECTINFO::iterator itip=it->second.begin();
			while(itip!=it->second.end()){
				MAPCONNECTRESULTINFO::iterator itresult=result.find(*itip);
				if(itresult==result.end()){
					IPList.insert(*itip);
				}else
				if((itresult->second.time<1)||(itresult->second.time>100)){
					IPList.insert(*itip);
				}				
				itip++;
			}
		}
		if(IPList.empty()){
			WARN("Connect 源IP:%s 已无需额外探测",it->first.c_str());
			it++;
			continue;
		}else{
			INFO("Connect 要探测数量:%zd 过滤后探测数量:%zd",it->second.size(),IPList.size());
		}
		ReSetStartTime();
		MAPCONNECTRESULTINFO tmp=GetConnectInfoByIPList(IPList,MaxWaitList,MaxTimeOut,it->first.c_str());
		if(result.empty()){
			result=tmp;
		}else{
			MAPCONNECTRESULTINFO::iterator it2=tmp.begin();
			while(it2!=tmp.end()){
				MAPCONNECTRESULTINFO::iterator itresult=result.find(it2->first);
				if(itresult==result.end()){
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it2->first,it2->second));
				}else
	//上次值无效取本次
				if(itresult->second.time<1){
					itresult->second=it2->second;
				}
	//当次值有效且低于上次只				
				if((it2->second.time>0)&&(it2->second.time<itresult->second.time)){
					itresult->second=it2->second;
				}
				it2++;
			}
		}
		it++;
    }
   return result;
}

void InitOpenMAX(){
    static int bInit=0;
    if(bInit==0){
#ifndef __MINGW32__		
        INFO("epoll_data_t:%zd epoll_data_ex:%zd",
        sizeof(epoll_data_t),sizeof(epoll_data_ex));
        struct rlimit rlp={0,0};
        getrlimit(RLIMIT_OFILE,&rlp);
        INFO("修改前最大描述符状态:CUR:%lu MAX:%lu SYS:%ld",rlp.rlim_cur,rlp.rlim_max,sysconf(_SC_OPEN_MAX));
        if(rlp.rlim_cur<10240)rlp.rlim_cur=10240;
        if(rlp.rlim_max<10240)rlp.rlim_max=10240;
        setrlimit(RLIMIT_OFILE,&rlp);
        INFO("修改后最大描述符状态:CUR:%lu MAX:%lu SYS:%ld",rlp.rlim_cur,rlp.rlim_max,sysconf(_SC_OPEN_MAX));
#endif		
        bInit=1;
    }
}
MAPCONNECTRESULTINFO GetConnectInfoByIPListWithLocal(SETCONNECTINFO IPList,size_t MaxWaitList,int MaxTimeOut){
    SETIPLIST publiclist=GetPublicIPList();
    MAPCONNECTRESULTINFO result;
    if(publiclist.size()<2){
		result=GetConnectInfoByIPList(IPList,MaxWaitList,MaxTimeOut,NULL);
    }else{
		SETIPLIST::iterator it=publiclist.begin();
		while(it!=publiclist.end()){
			MAPCONNECTRESULTINFO tmp=GetConnectInfoByIPList(IPList,MaxWaitList,MaxTimeOut,it->c_str());
			MAPCONNECTRESULTINFO::iterator it2=tmp.begin();
			while(it2!=tmp.end()){
				if(it2->second.time>0&&it2->second.time<1000){
					IPList.erase(it2->first);
				}
				MAPCONNECTRESULTINFO::iterator it3=result.find(it2->first);
				if(it3==result.end()){
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it2->first,it2->second));
				}else
				if((it2->second.time>0)&&(it2->second.time<it3->second.time)){
					it3->second=it2->second;
				}
				it2++;
            }
			it++;
        }
    }
    return result;
}
MAPCONNECTRESULTINFO _GetConnectInfoByIPList(SETCONNECTINFO IPList,size_t MaxWaitList,int MaxTimeOut,const char*SrcIP){
    MAPCONNECTRESULTINFO result;//存储结果
    MAPCONNECTWAITINFO   waitlist;//等待队列
    if(IPList.empty()){
		INFO("Connect 数量:%zd 为空 源IP:%s",IPList.size(),SrcIP);
		return result;
    }
	timeval sendtimestamp={0,0};
	timeval checktimestamp={0,0};
	CPublicSocket	sock;
	if(sock.EpollCreate(MaxWaitList)!=0){
		return result;
	}	
	while((IPList.empty()==false)||(waitlist.empty()==false)){
//进行一次连接
//队列不为空且数量未到达峰值
		if((IPList.empty()==false)&&(waitlist.size()<MaxWaitList)){	
			timeval timestamp={0,0};
			gettimeofday(&timestamp,NULL);
//1s1000次
			if((sendtimestamp.tv_sec!=timestamp.tv_sec)||
			   ((sendtimestamp.tv_usec/1000)!=(timestamp.tv_usec/1000))){
				sendtimestamp=timestamp;
				ConnectSysInfo SysInfo;
				memset(&SysInfo,0,sizeof(ConnectSysInfo));		
				CONNECTKEY KeyInfo;
				memset(&KeyInfo,0,sizeof(CONNECTKEY));
				do{
					SETCONNECTINFO::iterator it=IPList.begin();
					KeyInfo=*it;
					IPList.erase(KeyInfo);
				}while(0);				
				SysInfo.timestart=timestamp;
				int ret=sock.EpollConnect(KeyInfo.IP,KeyInfo.Port,SrcIP);
				if(ret<1){
					CONNECTVAL val={-1,0};
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(KeyInfo,val));
				}else
				if(ret==0){
					CONNECTVAL val={1,1};
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(KeyInfo,val));
				}else{
					SysInfo.fd=ret;
					waitlist.insert(std::pair<CONNECTKEY,ConnectSysInfo>(KeyInfo,SysInfo));
				}							
            }
        }
//检测接收队列
		do{
			timeval timeend={0};
			gettimeofday(&timeend,NULL);
			int n=0;
			struct epoll_event*events=sock.EpollWait(0,n);		
			int i=0;
			for(i=0;i<n;i++){
				epoll_data_ex*ptr=(epoll_data_ex*)&events[i].data;
				CONNECTKEY KeyInfo;
				memset(&KeyInfo,0,sizeof(CONNECTKEY));
				//通过fd查询
				//GetIPByHostID(KeyInfo.IP,ptr->data.addr);
				KeyInfo.Port=ptr->data.port;
				MAPCONNECTWAITINFO::iterator it=waitlist.begin();
				while(it!=waitlist.end()){
					if(it->second.fd==ptr->data.fd){
						if(it->first.Port==ptr->data.port){
							strcpy(KeyInfo.IP,it->first.IP);
							break;
						}else{
							WRONG("FD记录端口不一致 记录:%s:%hu 实际端口:%hu",
							ptr->data.fd,it->first.IP,it->first.Port,ptr->data.port);
						}
					}
					it++;
				}
				if(it!=waitlist.end()){
					CONNECTVAL val={0,0};
					val.time=val.tcping=GetTimerSubCommon(timeend,it->second.timestart);
					if(events[i].events!=EPOLLOUT){
						val.time=-1;
						//正常永不会出现,概率出现
						//WRONG("IP:%s:%hu@%d 连接错误:%u",KeyInfo.IP,ptr->data.port,ptr->data.fd,events[i].events);
                    }
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it->first,val));
					waitlist.erase(KeyInfo);
				}else{
//不应该有这错误
					WRONG("IP:%s:%hu@%d 不在接收队列中",KeyInfo.IP,ptr->data.port,ptr->data.fd);
                }				
				sock.EpollDel(ptr->data.fd);
            }
		}while(0);
//检测超时队列
		do{
			timeval timestamp={0,0};
			gettimeofday(&timestamp,NULL);
//1s定时清理一次
			if( checktimestamp.tv_sec!=timestamp.tv_sec){
				checktimestamp=timestamp;
				INFO("进行一次TCP超时检测 发送剩余:%zd 待确认数:%zd",
				IPList.size(),waitlist.size());
				MAPCONNECTWAITINFO waitlistbak=waitlist;
				waitlist.clear();
				MAPCONNECTWAITINFO::iterator it=waitlistbak.begin();
				while(it!=waitlistbak.end()){
					if((timestamp.tv_sec-it->second.timestart.tv_sec)>MaxTimeOut){
						CONNECTVAL val={0,0};
						result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it->first,val));
						sock.EpollDel(it->second.fd);					
					}else{
						waitlist.insert(std::pair<CONNECTKEY,ConnectSysInfo>(it->first,it->second));
                    }
					it++;
                }
				if(EnablePingThread()==0){
					INFO("收到停止信号 Connect 退出");
					break;
				}
            }
		}while(0);
//0.1ms		
		usleep(100);
	}	
	return result;
}
MAPCONNECTRESULTINFO GetTcpingByIPList(SETIPLIST IPList,SETPORTLIST PortList,int bReuse){
    MAPCONNECTRESULTINFO result;
    INFO("IP数量:%zd 端口数量:%zd",IPList.size(),PortList.size());
    SETPORTLIST::iterator itport=PortList.begin();
    while(itport!=PortList.end()){
		int Port=*itport;
		SETCONNECTINFO connectlist;
		SETIPLIST::iterator itip=IPList.begin();
		while(itip!=IPList.end()){
			CONNECTKEY keyinfo;
			memset(&keyinfo,0,sizeof(CONNECTKEY));
			strcpy(keyinfo.IP,itip->c_str());
			keyinfo.Port=Port;
			connectlist.insert(keyinfo);
			itip++;
        }
		INFO("当前遍历端口:%d IP数量:%zd",Port,IPList.size());
		MAPCONNECTRESULTINFO tmp=GetConnectInfoByIPList(connectlist,4000,5,NULL);
		MAPCONNECTRESULTINFO::iterator it=tmp.begin();
		while(it!=tmp.end()){
//复用			
			if(bReuse){
				result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it->first,it->second));
			}else{
				if(it->second.time>0){
					result.insert(std::pair<CONNECTKEY,CONNECTVAL>(it->first,it->second));
					IPList.erase(it->first.IP);
				}
			}
			it++;
        }
		itport++;
    }
    return result;
}
#if USE_PUBLIC_XLS
#include"PublicXls.h"
#endif
void ShowConnectStatusInfo(MAPCONNECTRESULTINFO IPList,bool bSave,bool bShow){
    MAPCONNECTRESULTINFO::iterator it=IPList.begin();
#if USE_PUBLIC_XLS	
    PublicXls xlsfile;
    if(bSave){
		char FileName[32]={0};
		sprintf(FileName,"ConnectInfo_%ld.xls",time(NULL));
		xlsfile.Xls_CreateFile(FileName);
		xlsfile.Xls_CreateSheet("CONNECT");
		xlsfile.Xls_WriteRawData("ssss","IP","PORT","Time(ms)","Tcping(ms)");
    }
#endif	
    while(it!=IPList.end()){
#if USE_PUBLIC_XLS			
		if(bSave){
			xlsfile.Xls_WriteRawData("sddd",it->first.IP,it->first.Port,it->second.time,it->second.tcping);
        }
#endif		
		if(bShow){
			if(it->second.time>0){
				INFO("IP:%s:%hu Time:%d ms",it->first.IP,it->first.Port,it->second.time);
			}else
			if(it->second.time==0){
				WARN("IP:%s:%hu TimeOut",it->first.IP,it->first.Port);
			}else{
				WARN("IP:%s:%hu ReFused Time:%d ms",it->first.IP,it->first.Port,it->second.tcping);
            }
        }
		it++;
    }
#if USE_PUBLIC_XLS	
    if(bSave){
		xlsfile.Xls_CloseSheet();
		xlsfile.Xls_CloseFile();
    }
#endif	
}
#if USE_PUBLIC_UDP
void ShowUDPStatusInfo(MAPCONNECTRESULTINFO IPList,bool bSave,bool bShow){
    MAPCONNECTRESULTINFO::iterator it=IPList.begin();
    PublicXls xlsfile;
    if(bSave){
		char FileName[32]={0};
		sprintf(FileName,"UdpInfo_%ld.xls",time(NULL));
		xlsfile.Xls_CreateFile(FileName);
		xlsfile.Xls_CreateSheet("UDP");
		xlsfile.Xls_WriteRawData("sss","IP","PORT","Close");
    }
    while(it!=IPList.end()){
		if(bSave){
			xlsfile.Xls_WriteRawData("sdd",it->first.IP,it->first.Port,it->second);
        }
		if(bShow){
			if(it->second>0){
				WARN("IP:%s:%hu Close",it->first.IP,it->first.Port);
			}else{
				WARN("IP:%s:%hu Open",it->first.IP,it->first.Port);
            }
        }
		it++;
    }
    if(bSave){
		xlsfile.Xls_CloseSheet();
		xlsfile.Xls_CloseFile();
    }
}
#endif
#endif
